﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Interpreter
{
	internal class ClassUtil
	{
		// Static methods exist on the class Value
		// Instance methods exist on the instance Value
		// They are lazily initialized. By the time this function is called, the
		// method has been verified with metadata to be a valid method.
		// Once they are initialized, they are cached on their object.
		public static Value GetMethod(Metadata metadata, int classId, int stepId, int memberId, Value context)
		{
			Value clazz = metadata.Classes[classId];
			Dictionary<int, Value> lookup = (Dictionary<int, Value>)(context ?? clazz).Value1;
			Value method;
			if (!lookup.TryGetValue(stepId, out method))
			{
				MethodSignature methodSig = (context == null ? metadata.StaticMethods : metadata.Methods)[memberId];
				method = new Value(Value.TYPE_FUNCTION, methodSig, context); // null context for static methods

				lookup[stepId] = method;
			}
			return method;
		}


		public static bool ExtendsFromSystemException(Metadata metadata, Value value)
		{
			int classId = (int)value.Value0;
			int systemException = metadata.SystemExceptionClassId;

			if (classId == systemException) return true;

			int[] ancestry = metadata.ClassAncestry[classId];

			// This loops rather than checks the last element directly for two reasons:
			// - future-proof in case I want to make System.Exception extend from something.
			// - there has to be a condition anyway since most classes will not have any parent class.
			for (int i = ancestry.Length - 1; i >= 0; --i)
			{
				if (ancestry[i] == systemException)
				{
					return true;
				}
			}

			return false;
		}

		public static string GetName(Metadata metadata, int classId)
		{
			int[] info = metadata.ClassesFast[classId];
			int stepId = info[0];
			string[] idTable = metadata.IdTable;
			string name = idTable[stepId];
			int c = classId;
			while (true)
			{
				if (info[1] != 0)
				{
					c = info[1];
					break;
				}
				else if (info[2] != 0)
				{
					c = info[2];
					name = idTable[c] + "." + name;
					info = metadata.ClassesFast[c];
				}
				else
				{
					break;
				}
			}

			int[][] namespaces = metadata.Namespaces;
			while (c != 0)
			{
				name = idTable[namespaces[c][0]] + "." + name;
				c = namespaces[c][1];
			}

			return name;
		}

		public static Value GetField(Metadata metadata, Value instance, string field)
		{
			Dictionary<int, Value> fields = (Dictionary<int, Value>)instance.Value1;
			int id = metadata.IdReverseTable[field];
			return fields[id];
		}

		public static void SetField(Metadata metadata, Value instance, string field, Value value)
		{
			Dictionary<int, Value> fields = (Dictionary<int, Value>)instance.Value1;
			int id = metadata.IdReverseTable[field];
			fields[id] = value;
		}
	}
}
